Skip to content

feat: add chainbench grpc subcommand for Solana Yellowstone gRPC benchmarks#121

Open
CSFeo wants to merge 2 commits into
chainstacklabs:mainfrom
CSFeo:feat/grpc-integration
Open

feat: add chainbench grpc subcommand for Solana Yellowstone gRPC benchmarks#121
CSFeo wants to merge 2 commits into
chainstacklabs:mainfrom
CSFeo:feat/grpc-integration

Conversation

@CSFeo

@CSFeo CSFeo commented Jul 15, 2026

Copy link
Copy Markdown

What

Adds a chainbench grpc subcommand for benchmarking Solana Yellowstone (Geyser)
gRPC endpoints, and bundles the chainbench-grpc
tool into the Docker image so it works out of the box.

chainbench grpc latency    --url URL --token TOKEN
chainbench grpc race       -u URL1 -t T1 -u URL2 -t T2 --transactions 5000
chainbench grpc throughput --url URL --token TOKEN --duration 60
chainbench grpc slots      --url URL --token TOKEN --target-slots 100
chainbench grpc full       ...
chainbench grpc latency --help    # rendered by the binary

How it works

  • chainbench/grpc.py — a Click subgroup with the five modes (race,
    latency, throughput, slots, full). It's a thin pass-through: it
    subprocess-invokes the bundled chainbench-grpc binary, forwards stdout /
    stderr and the exit code, and passes flags after the mode straight to the
    binary (which owns its own validation and --help). If the binary isn't on
    PATH it raises a clear ClickException.
  • chainbench/main.py — registers the subgroup (+4 lines).
  • Dockerfile — a Rust build stage compiles chainbench-grpc from a pinned
    tag (v0.4.0; protoc is vendored by the crate) and copies the binary to
    /usr/local/bin in the prod image.
  • README.md — documents the new command.

Testing

  • black --check, isort --check, flake8, mypy — clean on the changed files.
  • Verified end-to-end locally: installed ChainBench in a venv, put the built
    chainbench-grpc binary on PATH, and confirmed chainbench grpc <mode>
    shells out to it (correct output + exit codes; graceful "not found" message
    when the binary is absent).

Notes for maintainers

  • This introduces a build-time dependency on the (now public) CSFeo/chainbench-grpc
    repo, pinned to tag v0.4.0 via the CHAINBENCH_GRPC_REF build arg.
  • Follow-ups worth deciding: moving chainbench-grpc under the chainstacklabs
    org, and the published image / Docker Hub ownership.
  • The grpc command output is the tool's own (console/JSON/CSV/HTML); it is not
    normalized into the Locust report format.

Summary by CodeRabbit

  • New Features

    • Added a chainbench grpc command with sub-modes: race, latency, throughput, slots, and full.
    • Forwards arguments after the selected mode to the underlying gRPC benchmarking tool and returns its exit status.
    • Docker images now bundle the chainbench-grpc executable for use via chainbench grpc.
  • Documentation

    • Updated the README with “Solana Yellowstone gRPC benchmarks” usage, supported modes, and example invocations, including guidance for running outside Docker.

@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

The PR adds chainbench grpc modes, forwards arguments to chainbench-grpc, builds and bundles the binary in Docker, and documents usage and installation requirements.

gRPC benchmark integration

Layer / File(s) Summary
CLI wrapper and command registration
chainbench/grpc.py, chainbench/main.py
Adds Click commands for benchmark modes, forwards arguments, preserves the binary exit code, and registers the grpc group with the main CLI.
Docker build and runtime packaging
Dockerfile
Builds the Rust binary from a pinned repository reference and copies it into the production image’s executable path.
Command usage documentation
README.md
Documents supported modes, argument forwarding, example commands, and installation outside Docker.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant grpc_wrapper
  participant chainbench_grpc
  CLI->>grpc_wrapper: invoke grpc mode with arguments
  grpc_wrapper->>grpc_wrapper: locate chainbench-grpc on PATH
  grpc_wrapper->>chainbench_grpc: execute mode and forwarded arguments
  chainbench_grpc-->>grpc_wrapper: return exit code
  grpc_wrapper-->>CLI: exit with binary return code
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description is detailed, but it does not use the required ## Description and ## Issues Resolved sections from the template. Reformat the PR description to match the template with ## Description and ## Issues Resolved, and summarize the resolved issue(s) under the latter.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the new chainbench grpc subcommand for Solana Yellowstone gRPC benchmarks.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
chainbench/grpc.py (1)

31-31: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Catch OSError for robust binary execution.

While shutil.which ensures the binary exists and is marked as executable, subprocess.run can still raise an OSError (e.g., ENOEXEC due to a CPU architecture mismatch if a user downloads the wrong local binary). Catching this exception and raising a click.ClickException provides a cleaner user experience than a raw Python traceback.

🛠️ Proposed refactor to handle execution errors gracefully
-    completed = subprocess.run([exe, mode, *args])
+    try:
+        completed = subprocess.run([exe, mode, *args])
+    except OSError as e:
+        raise click.ClickException(f"Failed to execute `{exe}`: {e}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chainbench/grpc.py` at line 31, Update the subprocess.run invocation in the
binary execution flow to catch OSError, including execution failures such as
incompatible binaries, and raise a click.ClickException with a clear error
message instead of exposing the raw traceback. Preserve the existing command
arguments and successful execution behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@chainbench/grpc.py`:
- Line 31: Update the subprocess.run invocation in the binary execution flow to
catch OSError, including execution failures such as incompatible binaries, and
raise a click.ClickException with a clear error message instead of exposing the
raw traceback. Preserve the existing command arguments and successful execution
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e2d0096b-3629-490d-81d8-a08a120d0d99

📥 Commits

Reviewing files that changed from the base of the PR and between 5d56c2d and 64da86e.

📒 Files selected for processing (4)
  • Dockerfile
  • README.md
  • chainbench/grpc.py
  • chainbench/main.py

…nchmarks

Integrate the chainbench-grpc tool as a CLI invocation inside the ChainBench
Docker image (per the Solana gRPC benchmarking initiative).

- chainbench/grpc.py: `chainbench grpc <race|latency|throughput|slots|full>`
  Click subgroup. Thin pass-through — shells out to the bundled `chainbench-grpc`
  binary, forwards stdout/stderr + exit code; flags after the mode go straight to
  the binary (it owns its own validation and --help). Clear error if not on PATH.
- chainbench/main.py: register the subgroup.
- Dockerfile: Rust build stage builds chainbench-grpc from a pinned tag (v0.4.0)
  and copies the binary onto PATH in the prod image (protoc is vendored).
- README: document the new `grpc` command.

Tested: black/isort/flake8/mypy clean; the subcommand shells out to the real
binary end to end (verified locally), with correct exit codes and a graceful
"binary not found" message.
@CSFeo
CSFeo force-pushed the feat/grpc-integration branch from 64da86e to 7e40a12 Compare July 15, 2026 01:06
@CSFeo

CSFeo commented Jul 24, 2026

Copy link
Copy Markdown
Author

@erwin-wee could you take a look when you have a moment? 🙏

This adds a chainbench grpc <race|latency|throughput|slots|full> subcommand that shells out to the bundled chainbench-grpc binary — the Docker image builds it from a pinned tag (v0.4.0) of chainstacklabs/chainbench-grpc (just updated the URLs now that the repo lives under the org). No rewrite, same single image/UX.

Verified end-to-end: built the image and ran chainbench grpc latency inside the container against the bundled binary; black/isort/flake8/mypy clean on the changed files. Happy to iterate on anything.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
chainbench/grpc.py (1)

31-32: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Use exec for a true pass-through wrapper.

subprocess.run can handle ordinary exit codes, but signals such as Ctrl-C may be handled by the Python/Click parent instead of preserving the benchmark process semantics. After the existence check, os.execv(exe, [exe, mode, *args]) would inherit stdio, signals, and the child’s exit status directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@chainbench/grpc.py` around lines 31 - 32, Replace the subprocess.run and
sys.exit flow in the wrapper entry point with os.execv, passing exe as both the
executable and argv[0] followed by mode and args, so the wrapper directly
preserves the benchmark process’s stdio, signals, and exit status.
Dockerfile (1)

6-8: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Use an immutable commit instead of a mutable tag.

v0.4.0 can be retagged, and CHAINBENCH_GRPC_REF is overrideable at build time. Resolve the release to a full commit SHA (and verify it in CI) so production images remain reproducible.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 6 - 8, Update the Dockerfile’s CHAINBENCH_GRPC_REF
clone configuration to use the full immutable commit SHA for the intended v0.4.0
release instead of a mutable tag, and prevent build-time overrides from changing
that pinned revision. Ensure CI verifies the resolved commit matches the
expected SHA before producing production images.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Dockerfile`:
- Around line 35-36: Create a dedicated non-root runtime user in the production
image, ensure it can access the required application files and binaries, and add
the user switch before the existing ENTRYPOINT. Keep the chainbench-grpc PATH
setup unchanged while ensuring the production CLI runs without root privileges.

---

Nitpick comments:
In `@chainbench/grpc.py`:
- Around line 31-32: Replace the subprocess.run and sys.exit flow in the wrapper
entry point with os.execv, passing exe as both the executable and argv[0]
followed by mode and args, so the wrapper directly preserves the benchmark
process’s stdio, signals, and exit status.

In `@Dockerfile`:
- Around line 6-8: Update the Dockerfile’s CHAINBENCH_GRPC_REF clone
configuration to use the full immutable commit SHA for the intended v0.4.0
release instead of a mutable tag, and prevent build-time overrides from changing
that pinned revision. Ensure CI verifies the resolved commit matches the
expected SHA before producing production images.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 35b52e6a-9b40-420e-91d1-a1daf4587b57

📥 Commits

Reviewing files that changed from the base of the PR and between 64da86e and 4584e9b.

📒 Files selected for processing (4)
  • Dockerfile
  • README.md
  • chainbench/grpc.py
  • chainbench/main.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • README.md
  • chainbench/main.py

Comment thread Dockerfile
Comment on lines +35 to +36
# Bundle the chainbench-grpc binary on PATH (used by `chainbench grpc`).
COPY --from=grpc /src/target/release/chainbench-grpc /usr/local/bin/chainbench-grpc

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Run the production image as a non-root user.

The new binary is added to an image that still defaults to root. Create a dedicated runtime user and switch to it before ENTRYPOINT; otherwise vulnerabilities in the CLI, benchmark binary, or dependencies have root privileges.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Dockerfile` around lines 35 - 36, Create a dedicated non-root runtime user in
the production image, ensure it can access the required application files and
binaries, and add the user switch before the existing ENTRYPOINT. Keep the
chainbench-grpc PATH setup unchanged while ensuring the production CLI runs
without root privileges.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant